package com.example.lotterysystem.service.mq;

import cn.hutool.core.date.DateUtil;
import com.example.lotterysystem.common.exception.ServiceException;
import com.example.lotterysystem.common.utils.JacksonUtil;
import com.example.lotterysystem.common.utils.MailUtil;
import com.example.lotterysystem.common.utils.SMSUtil;
import com.example.lotterysystem.controller.param.DrawPrizeParam;
import com.example.lotterysystem.dao.dataobject.ActivityPrizeDO;
import com.example.lotterysystem.dao.dataobject.WinningRecordDO;
import com.example.lotterysystem.dao.mapper.ActivityPrizeMapper;
import com.example.lotterysystem.dao.mapper.WinnerRecordMapper;
import com.example.lotterysystem.service.DrawPrizeService;
import com.example.lotterysystem.service.activitystatus.ActivityStatusManager;
import com.example.lotterysystem.service.dto.ConvertActivityStatusDTO;
import com.example.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.example.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.example.lotterysystem.service.enums.ActivityStatusEnum;
import com.example.lotterysystem.service.enums.ActivityUserStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.example.lotterysystem.common.config.DirectRabbitConfig.QUEUE_NAME;
import static com.example.lotterysystem.service.DrawPrizeService.MESSAGE_DATA;

@Slf4j
@Component
@RabbitListener(queues = QUEUE_NAME)
public class MQReceiver {
    @Autowired
    private DrawPrizeService drawPrizeService;
    @Autowired
    private ActivityStatusManager activityStatusManager;
    @Autowired
    private MailUtil mailUtil;
    @Autowired
    private SMSUtil smsUtil;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private WinnerRecordMapper winnerRecordMapper;

    @RabbitHandler
    public void process(Map<String, String> message) {
        // 日志打印
        String paramString = message.get(MESSAGE_DATA);
        log.info("process 成功从队列 {} 中接收到消息 {}", QUEUE_NAME, paramString);
        // 获取请求参数
        DrawPrizeParam drawPrizeParam = JacksonUtil.readValue(paramString, DrawPrizeParam.class);
        try {
            // 校验抽奖请求是否有效
            if (!drawPrizeService.checkDrawPrizeValid(drawPrizeParam)) {
                return;
            }
            // 进行状态扭转
            statusConvert(drawPrizeParam);
            // 保存中奖名单信息
            List<WinningRecordDO> winningRecordDOList =
                    drawPrizeService.saveWinnerRecords(drawPrizeParam);
            // 通知中奖者
            sendMail(winningRecordDOList);
        } catch (ServiceException e) {
            log.warn("MQ 消息处理异常! {}: {}", e.getCode(), e.getMessage());
            // 回滚
            rollback(drawPrizeParam);
            throw e; // 重新抛出异常，MQ 进行重发等操作
        } catch (Exception e) {
            log.warn("MQ 消息处理异常!", e);
            // 回滚
            rollback(drawPrizeParam);
            throw e; // 重新抛出异常，MQ 进行重发等操作
        }
    }

    /**
     * 进行回滚操作
     * @param drawPrizeParam
     */
    private void rollback(DrawPrizeParam drawPrizeParam) {
        // 判断 活动、人员、奖品是否被修改
        // 在进行修改时保证了事务的一致性，只需要判断 奖品 或 人员是否被修改
        if (!statusNeedRollback(drawPrizeParam)) {
            return;
        }
        rollbackStatus(drawPrizeParam);
        // 判断中奖记录是否落库
        if (!winningRecordNeedRollback(drawPrizeParam)) {
            return;
        }
        rollbackWinningRecord(drawPrizeParam);
    }

    /**
     * 回滚中奖记录
     * @param drawPrizeParam
     */
    private void rollbackWinningRecord(DrawPrizeParam drawPrizeParam) {
        // 回滚中奖记录
        drawPrizeService.deleteRecords(
                drawPrizeParam.getActivityId(), drawPrizeParam.getPrizeId());
    }

    /**
     * 判断中奖记录是否需要回滚
     * @param drawPrizeParam
     * @return
     */
    private boolean winningRecordNeedRollback(DrawPrizeParam drawPrizeParam) {
        int count = winnerRecordMapper.countByAPId(drawPrizeParam.getActivityId(),
                drawPrizeParam.getPrizeId());
        return count > 0;
    }

    /**
     * 将 活动、人员、奖品状态进行回滚
     * @param drawPrizeParam
     */
    private void rollbackStatus(DrawPrizeParam drawPrizeParam) {
        ConvertActivityStatusDTO convertActivityStatusDTO = new ConvertActivityStatusDTO();
        convertActivityStatusDTO.setActivityId(drawPrizeParam.getActivityId());
        convertActivityStatusDTO.setTargetActivityStatus(ActivityStatusEnum.RUNNING);
        convertActivityStatusDTO.setPrizeId(drawPrizeParam.getPrizeId());
        convertActivityStatusDTO.setTargetPrizeStatus(ActivityPrizeStatusEnum.INIT);
        convertActivityStatusDTO.setUserIds(
                drawPrizeParam.getWinnerList().stream()
                        .map(DrawPrizeParam.Winner::getUserId)
                        .collect(Collectors.toList())
        );
        convertActivityStatusDTO.setTargetUserStatus(ActivityUserStatusEnum.INIT);
        activityStatusManager.rollbackHandlerEvent(convertActivityStatusDTO);
    }

    /**
     * 判断活动、人员、奖品状态是否需要回滚
     * @param drawPrizeParam
     * @return
     */
    private boolean statusNeedRollback(DrawPrizeParam drawPrizeParam) {
        // 只需判断奖品状态是否被修改
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(drawPrizeParam.getActivityId(),
                drawPrizeParam.getPrizeId());
        return ActivityPrizeStatusEnum.COMPLETED.name()
                .equalsIgnoreCase(activityPrizeDO.getStatus());
    }

    /**
     * 发送邮件
     * @param winningRecordDOList
     */
    private void sendMail(List<WinningRecordDO> winningRecordDOList) {
        if (CollectionUtils.isEmpty(winningRecordDOList)) {
            log.info("发送邮件失败，中奖列表为空");
            return;
        }
        for (WinningRecordDO winningRecordDO: winningRecordDOList) {
            String context = "Hi，" + winningRecordDO.getWinnerName() + "。恭喜你在"
                    + winningRecordDO.getActivityName() + "活动中获得"
                    + ActivityPrizeTiersEnum.forName(winningRecordDO.getPrizeTier()).getMessage()
                    + "：" + winningRecordDO.getPrizeName() + "。获奖时间为"
                    + DateUtil.formatTime(winningRecordDO.getWinningTime()) + "，请尽快领 取您的奖励！";
            mailUtil.sendSampleMail(winningRecordDO.getWinnerEmail(),
                    "中奖通知", context);
        }
    }

    /**
     * 发送短信
     * @param winningRecordDOList
     */
     private void sendMessage(List<WinningRecordDO> winningRecordDOList) {
         if (CollectionUtils.isEmpty(winningRecordDOList)) {
             log.info("发送短信失败，中奖列表为空");
             return;
         }
         for (WinningRecordDO winningRecordDO: winningRecordDOList) {
             Map<String, String> map = new HashMap<>();
             map.put("name", winningRecordDO.getWinnerName());
             map.put("activityName", winningRecordDO.getActivityName());
             map.put("prizeTiers", winningRecordDO.getPrizeTier());
             map.put("prizeName", winningRecordDO.getPrizeName());
             map.put("winningTime", DateUtil.formatTime(winningRecordDO.getWinningTime()));
             smsUtil.sendMessage("SMS_476715195",
                     winningRecordDO.getWinnerPhoneNumber().getPhone(),
                     JacksonUtil.writeValueAsString(map));
         }
     }
    /**
     * 状态转换
     * @param drawPrizeParam
     */
    private void statusConvert(DrawPrizeParam drawPrizeParam) {
        // 构造请求
        ConvertActivityStatusDTO convertActivityStatusDTO = new ConvertActivityStatusDTO();
        convertActivityStatusDTO.setActivityId(drawPrizeParam.getActivityId());
        convertActivityStatusDTO.setTargetActivityStatus(ActivityStatusEnum.COMPLETED);
        convertActivityStatusDTO.setPrizeId(drawPrizeParam.getPrizeId());
        convertActivityStatusDTO.setTargetPrizeStatus(ActivityPrizeStatusEnum.COMPLETED);
        convertActivityStatusDTO.setUserIds(
                drawPrizeParam.getWinnerList().stream()
                        .map(DrawPrizeParam.Winner::getUserId)
                        .collect(Collectors.toList())
        );
        convertActivityStatusDTO.setTargetUserStatus(ActivityUserStatusEnum.COMPLETED);
        // 进行状态转换
        activityStatusManager.handlerEvent(convertActivityStatusDTO);
    }
}
